#ifndef __EPOLL_SERVER_HPP__
#define __EPOLL_SERVER_HPP__

#include <iostream>
#include <memory>
#include <unistd.h>
#include "log.hpp"
#include "epoll.hpp"
#include "MySock.hpp"


// epoll服务器
class EpollServer
{
    using func_t = std::function<void(std::string)>;
    const int events_lens = 64;
    const uint16_t default_port = 8080;
private:
    uint16_t _port;
    int _listenSock;
    int _epfd;
    struct epoll_event* _events;
    func_t _handlerRequest;
private:
    // 连接处理函数
    void accepter(int fd)
    {
        std::string clientIp;
        uint16_t clientPort;
        int sock = QiHai::Sock::accept(fd, &clientIp, &clientPort);  // fd == _listenSock
        // 获取了新连接， 加入监听中 - 读取
        if (QiHai::epoll::ctl(_epfd, EPOLL_CTL_ADD, sock, EPOLLIN) < 0) exit(5);
    }

    // 读取处理函数
    void reader(int fd)
    {
        char buffer[10240];
        int n = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            _handlerRequest(buffer);
        }
        else if (n == 0)
        {
            logMessage(WARNING, "client close sock, me too!");
            // 首先取消关系，然后再关闭文件描述符，否则可能出现问题
            if (QiHai::epoll::ctl(_epfd, EPOLL_CTL_DEL, fd) < 0) exit(5);
            close(fd);
        }
        else{
            // 读取失败
            logMessage(FATAL, "recv error %d:%s", errno, strerror(errno));
            if (QiHai::epoll::ctl(_epfd, EPOLL_CTL_DEL, fd) < 0) exit(5);
            close(fd);
        }
    }

    void handlerEvents(int n)
    {
        for (int i = 0; i < n; ++i)
        {
            int sock = _events[i].data.fd;
            if (sock == _listenSock) accepter(sock);
            else if (_events[i].events == EPOLLIN) reader(sock);
            // else if (_events[i].events == EPOLLOUT) //......
        }
    }

    // 循环一次，给出等待时间 >0 对应时间进行阻塞等待，=0非阻塞等待 <0阻塞等待
    void loopOnce(int timeout = -1)  // 默认非阻塞
    {
        // 根据设置时间进行等待
        int n = QiHai::epoll::wait(_epfd, _events, events_lens, timeout);
        switch (n)
        {
        case 0:
            logMessage(NORMAL, "wait......");
            break;
        case -1:
            logMessage(FATAL, "epoll_wait error %d:%s", errno, strerror(errno));
            exit(6);
            break;
        default:
            // 等待成功
            logMessage(DEBUG, "epoll_wait have events!");
            handlerEvents(n);  // 处理就绪的事件
            break;
        }
    }
public:
    EpollServer(func_t handlerRequest)  // 注册对应的回调方法void(string)
    :_port(default_port), _events(nullptr), _handlerRequest(handlerRequest)
    {
        // 首先创建套接字
        _listenSock = QiHai::Sock::socket();
        if (_listenSock < 0) exit(1);

        // 对此套接字进行绑定
        if(!QiHai::Sock::bind(_listenSock, _port))   // 8080端口和0.0.0.0全局网络
            exit(2);
        
        // 对此套接字设置监听状态
        if (!QiHai::Sock::listen(_listenSock)) exit(3);

        // 创建epoll模型
        _epfd = QiHai::epoll::create();
        if (_epfd < 0) exit(4);  // 创建失败

        // 将监听套接字添加入epoll模型中，对其读取事件进行监听 - 即对应连接是否就绪
        if (QiHai::epoll::ctl(_epfd, EPOLL_CTL_ADD, _listenSock, EPOLLIN) < 0) 
            exit(5);  // 设置失败

        // 初始化接收就绪缓冲区，这里设置为定长
        _events = new epoll_event[events_lens];

        logMessage(DEBUG, "EpollServer init success......");
    }

    void Start()
    {
        int timeout = 5000;  // 阻塞5秒等待一次
        while (true)
        {
            loopOnce(timeout);
        }
    }

    ~EpollServer()
    {
        if (_listenSock >= 0) close(_listenSock);
        if (_epfd >= 0) close(_epfd);
        if (_events) delete[] _events;
    }
};


#endif